﻿// Inner Fire 游戏引擎库
// DxSwapchain 渲染器Swapchain管理器
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 用于DirectX的渲染器。
// 此文件分装Swapchain管理器
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-23

#include "dx_swapchain.h"
#include "dx_graphics.h"

namespace ifire::dx {
ifire::dx::DxSwapchain::DxSwapchain(DxGraphics* dx) : dx_(dx) {}

void DxSwapchain::CreateSwapChain() {
  // 需要释放之前的交换链，然后进行重建（在分辨率改变的时候会重建）
  swap_chain_.Reset();

  // 创建SwapChain的描述
  DXGI_SWAP_CHAIN_DESC desc = {};
  desc.BufferDesc.Width = dx_->GetWidth();
  desc.BufferDesc.Height = dx_->GetHeight();
  desc.BufferDesc.RefreshRate.Numerator = 60;
  desc.BufferDesc.RefreshRate.Denominator = 1;
  desc.BufferDesc.Format = BACK_BUFFER_FORMAT;
  desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
  desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

  // 设置MASS
  desc.SampleDesc.Count = DxUtils::GetMsaaSampleCount();
  desc.SampleDesc.Quality = DxUtils::GetMsaaSampleQuality();
  desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT;
  desc.OutputWindow = dx_->GetHwnd();
  desc.Windowed = true;
  desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
  desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

  // 创建SwapChain
  HASSERT(dx_->GetFactory()->CreateSwapChain(
      dx_->GetCmdQueue(), &desc, swap_chain_.GetAddressOf()));
}
void DxSwapchain::Reset() {
  // 清空指针
  for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++) {
    swap_chain_buffer_[i].Reset();
  }
  depth_stencil.Reset();

  // 重新设置SwapChainBuffer的大小
  HASSERT(swap_chain_->ResizeBuffers(SWAP_CHAIN_BUFFER_COUNT, dx_->GetWidth(),
      dx_->GetHeight(), BACK_BUFFER_FORMAT,
      DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));
  current_back_buffer_ = 0;
}

void DxSwapchain::CreateRenderTargetView() {
  // 得到Handle
  CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_heap_handle(
      dx_->GetDescriptor().rtv_heap_->GetCPUDescriptorHandleForHeapStart());

  // 需要为每一个SwapChain创建BUFFER
  for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++) {
    // 获得交换链中的第i个缓冲区
    HASSERT(swap_chain_->GetBuffer(i, IID_PPV_ARGS(&swap_chain_buffer_[i])));
    // 为此缓冲区创建一个RTV
    dx_->Device()->CreateRenderTargetView(
        swap_chain_buffer_[i].Get(), nullptr, rtv_heap_handle);
    // 偏移到下一个缓冲区
    rtv_heap_handle.Offset(1, dx_->GetDescriptor().RTV_SIZE);
  }
}

void DxSwapchain::CreateDepthStencilView() {
  // 配置缓冲区DESC
  D3D12_RESOURCE_DESC depth_stencil_desc = {};
  depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  depth_stencil_desc.Alignment = 0;
  depth_stencil_desc.Width = dx_->GetWidth();
  depth_stencil_desc.Height = dx_->GetHeight();
  depth_stencil_desc.DepthOrArraySize = 1;
  depth_stencil_desc.MipLevels = 1;
  depth_stencil_desc.Format = DEPTH_STENCIL_FORMAT;
  depth_stencil_desc.SampleDesc.Count = DxUtils::GetMsaaSampleCount();
  depth_stencil_desc.SampleDesc.Quality = DxUtils::GetMsaaSampleQuality();
  depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

  // 清除资源的优化值（如果选择适当的清除值，可以提高操作的速度）
  D3D12_CLEAR_VALUE opt_clear = {};
  opt_clear.Format = DEPTH_STENCIL_FORMAT;
  opt_clear.DepthStencil.Depth = 1.0f;
  opt_clear.DepthStencil.Stencil = 0;

  // 创建资源并提交到堆
  CD3DX12_HEAP_PROPERTIES props(D3D12_HEAP_TYPE_DEFAULT);
  HASSERT(dx_->Device()->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE,
      &depth_stencil_desc,
      D3D12_RESOURCE_STATE_COMMON, // 初始状态
      &opt_clear,                  // 清理资源优化值
      IID_PPV_ARGS(depth_stencil.GetAddressOf())));

  // 利用此资源的格式，为整个资源的第0 mip层创建描述符
  dx_->Device()->CreateDepthStencilView(
      depth_stencil.Get(), nullptr, dx_->GetDepthStencilView());

  // 将资源从初始状态转换为深度缓冲区
  auto rb = CD3DX12_RESOURCE_BARRIER::Transition(depth_stencil.Get(),
      D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE);
  dx_->CommandList()->ResourceBarrier(1, &rb);
}

ID3D12Resource* DxSwapchain::GetBackBuffer() const {
  return swap_chain_buffer_[current_back_buffer_].Get();
}

void DxSwapchain::PresentAndSwap() {
  // 交换后台与前台缓冲区
  HASSERT(swap_chain_->Present(0, 0));
  current_back_buffer_ = (current_back_buffer_ + 1) % SWAP_CHAIN_BUFFER_COUNT;
}

D3D12_CPU_DESCRIPTOR_HANDLE DxSwapchain::GetBackBufferView() const {
  return CD3DX12_CPU_DESCRIPTOR_HANDLE(
      dx_->GetDescriptor().rtv_heap_->GetCPUDescriptorHandleForHeapStart(),
      current_back_buffer_, dx_->GetDescriptor().RTV_SIZE);
}

} // namespace ifire::dx